const Direction = {
  Up: 'Up',
  Down: 'Down',
  Left: 'Left',
  Right: 'Right',
}

const ItemType = {
  Wall: 0,
  Road: 1,
  Aim: 2,
  Man: 3,
  NBox: 4,
  ABox: 5,
}

/*
  0 墙
  1 路
  2 目标
  3 人
  4 普通箱子
  5 达到目标的箱子
 */

class Scene {
  /**
   * 场景类
   * @param {Array} map
   * @param {[number, number]} man
   * @constructor
   */
  constructor(map, man) {
    if (map.length < 0) throw new Error('map 数组必须为长宽不小于1的二维数组！')
    this.map = map
    this.man = man
    this.width = map[0].length
    this.height = map.length
  }

  /**
   * 人物移动
   * @param {string} direction
   * @return {Boolean} 是否需要重新渲染场景
   */
  move(direction) {
    let [x, y] = this.man
    let map = this.map

    const isWall = (x, y) => map[y][x] === 0
    const isRoad = (x, y) => map[y][x] === 1
    const isBox = (x, y) => map[y][x] === 4 || map[y][x] === 5
    const isAim = (x, y) => map[y][x] === 2
    const isABox = (x, y) => map[y][x] === 5

    const whenNotBoxDo = (x, y) => {
      if (isWall(x, y)) return 2
      if (isRoad(x, y) || isAim(x, y)) {
        this.man = [x, y]
        return 1
      }
      return 0
    }

    const whenBoxDo = (x, y, boxX, boxY) => {
      if (
        boxX >= this.width || boxX < 0 ||
        boxY >= this.height || boxY < 0 ||
        isWall(boxX, boxY) || isBox(boxX, boxY)
      ) return false
      this.man = [x, y]
      this.map[y][x] = isABox(x, y) ? ItemType.Aim : ItemType.Road
      this.map[boxY][boxX] = isAim(boxX, boxY) ? ItemType.ABox : ItemType.NBox
      return true
    }

    switch (direction) {
      case Direction.Up: {
        y -= 1
        let boxY = y - 1
        let boxX = x
        if (y < 0) return false
        let flag = whenNotBoxDo(x, y)
        if (flag === 0) {
          if (isBox(x, y)) return whenBoxDo(x, y, boxX, boxY)
        } else {
          return [false, true, false][flag]
        }
        break
      }
      case Direction.Down: {
        y += 1
        let boxY = y + 1
        let boxX = x
        if (y >= this.height) return false
        let flag = whenNotBoxDo(x, y)
        if (flag === 0) {
          if (isBox(x, y)) return whenBoxDo(x, y, boxX, boxY)
        } else {
          return [false, true, false][flag]
        }
        break
      }
      case Direction.Left: {
        x -= 1
        let boxX = x - 1
        let boxY = y
        if (x < 0) return false
        let flag = whenNotBoxDo(x, y)
        if (flag === 0) {
          if (isBox(x, y)) return whenBoxDo(x, y, boxX, boxY)
        } else {
          return [false, true, false][flag]
        }
        break
      }
      case Direction.Right: {
        x += 1
        let boxX = x + 1
        let boxY = y
        if (x >= this.width) return false
        let flag = whenNotBoxDo(x, y)
        if (flag === 0) {
          if (isBox(x, y)) return whenBoxDo(x, y, boxX, boxY)
        } else {
          return [false, true, false][flag]
        }
        break
      }
    }

    return false
  }

  getScene () {
    return this.map
  }

  getMan () {
    return this.man
  }

  judgeWin () {
    for (let line of this.map) {
      if (line.includes(ItemType.NBox)) return false
    }
    return true
  }
}

